<script lang="ts" setup>
import CategoryFilterDropdown from "@/components/filter/CategoryFilterDropdown.vue";
import TagFilterDropdown from "@/components/filter/TagFilterDropdown.vue";
import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
import { postLabels } from "@/constants/labels";
import type { ListedPost, Post } from "@halo-dev/api-client";
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
import {
  Dialog,
  IconAddCircle,
  IconArrowLeft,
  IconArrowRight,
  IconBookRead,
  IconRefreshLine,
  Toast,
  VButton,
  VCard,
  VEmpty,
  VEntityContainer,
  VLoading,
  VPageHeader,
  VPagination,
  VSpace,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { useRouteQuery } from "@vueuse/router";
import { chunk } from "es-toolkit";
import type { Ref } from "vue";
import { computed, provide, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import PostBatchSettingModal from "./components/PostBatchSettingModal.vue";
import PostListItem from "./components/PostListItem.vue";
import PostSettingModal from "./components/PostSettingModal.vue";

const { t } = useI18n();

const settingModal = ref(false);
const selectedPost = ref<Post>();
const checkedAll = ref(false);
const selectedPostNames = ref<string[]>([]);

provide<Ref<string[]>>("selectedPostNames", selectedPostNames);

// Filters
const page = useRouteQuery<number>("page", 1, {
  transform: Number,
});
const size = useRouteQuery<number>("size", 20, {
  transform: Number,
});
const selectedVisible = useRouteQuery<
  "PUBLIC" | "INTERNAL" | "PRIVATE" | undefined
>("visible");
const selectedPublishStatus = useRouteQuery<string | undefined>("status");
const selectedSort = useRouteQuery<string | undefined>("sort");
const selectedCategory = useRouteQuery<string | undefined>("category");
const selectedTag = useRouteQuery<string | undefined>("tag");
const selectedContributor = useRouteQuery<string | undefined>("contributor");
const keyword = useRouteQuery<string>("keyword", "");
const total = ref(0);
const hasPrevious = ref(false);
const hasNext = ref(false);

watch(
  () => [
    selectedVisible.value,
    selectedPublishStatus.value,
    selectedSort.value,
    selectedCategory.value,
    selectedTag.value,
    selectedContributor.value,
    keyword.value,
  ],
  () => {
    page.value = 1;
  }
);

function handleClearFilters() {
  selectedVisible.value = undefined;
  selectedPublishStatus.value = undefined;
  selectedSort.value = undefined;
  selectedCategory.value = undefined;
  selectedTag.value = undefined;
  selectedContributor.value = undefined;
}

const hasFilters = computed(() => {
  return (
    selectedVisible.value ||
    selectedPublishStatus.value !== undefined ||
    selectedSort.value ||
    selectedCategory.value ||
    selectedTag.value ||
    selectedContributor.value
  );
});

const {
  data: posts,
  isLoading,
  isFetching,
  refetch,
} = useQuery<ListedPost[]>({
  queryKey: [
    "posts",
    page,
    size,
    selectedCategory,
    selectedTag,
    selectedContributor,
    selectedPublishStatus,
    selectedVisible,
    selectedSort,
    keyword,
  ],
  queryFn: async () => {
    const labelSelector: string[] = ["content.halo.run/deleted=false"];
    const fieldSelector: string[] = [];

    if (selectedCategory.value) {
      fieldSelector.push(`spec.categories=${selectedCategory.value}`);
    }

    if (selectedTag.value) {
      fieldSelector.push(`spec.tags=${selectedTag.value}`);
    }

    if (selectedContributor.value) {
      fieldSelector.push(`status.contributors=${selectedContributor.value}`);
    }

    if (selectedVisible.value) {
      fieldSelector.push(`spec.visible=${selectedVisible.value}`);
    }

    if (selectedPublishStatus.value !== undefined) {
      labelSelector.push(selectedPublishStatus.value);
    }

    const { data } = await consoleApiClient.content.post.listPosts({
      labelSelector,
      fieldSelector,
      page: page.value,
      size: size.value,
      sort: [selectedSort.value].filter(Boolean) as string[],
      keyword: keyword.value,
    });

    total.value = data.total;
    hasNext.value = data.hasNext;
    hasPrevious.value = data.hasPrevious;

    return data.items;
  },
  refetchInterval: (data) => {
    const hasDeletingPost = data?.some((post) => post.post.spec.deleted);

    if (hasDeletingPost) {
      return 1000;
    }

    const hasPublishingPost = data?.some((post) => {
      const { spec, metadata } = post.post;
      return (
        metadata.labels?.[postLabels.PUBLISHED] !== spec.publish + "" &&
        metadata.labels?.[postLabels.SCHEDULING_PUBLISH] !== "true"
      );
    });

    if (hasPublishingPost) {
      return 1000;
    }

    const hasCancelingPublishPost = data?.some((post) => {
      const { spec, metadata } = post.post;
      return (
        !spec.publish &&
        (metadata.labels?.[postLabels.PUBLISHED] === "true" ||
          metadata.labels?.[postLabels.SCHEDULING_PUBLISH] === "true")
      );
    });

    return hasCancelingPublishPost ? 1000 : false;
  },
});

const handleOpenSettingModal = async (post: Post) => {
  const { data } = await coreApiClient.content.post.getPost({
    name: post.metadata.name,
  });
  selectedPost.value = data;
  settingModal.value = true;
};

const onSettingModalClose = () => {
  selectedPost.value = undefined;
  settingModal.value = false;
  refetch();
};

const handleSelectPrevious = async () => {
  if (!posts.value) return;

  const index = posts.value.findIndex(
    (post) => post.post.metadata.name === selectedPost.value?.metadata.name
  );

  if (index > 0) {
    const { data: previousPost } = await coreApiClient.content.post.getPost({
      name: posts.value[index - 1].post.metadata.name,
    });
    selectedPost.value = previousPost;
    return;
  }
  if (index === 0 && hasPrevious.value) {
    page.value--;
    await refetch();
    selectedPost.value = posts.value[posts.value.length - 1].post;
  }
};

const handleSelectNext = async () => {
  if (!posts.value) return;

  const index = posts.value.findIndex(
    (post) => post.post.metadata.name === selectedPost.value?.metadata.name
  );
  if (index < posts.value.length - 1) {
    const { data: nextPost } = await coreApiClient.content.post.getPost({
      name: posts.value[index + 1].post.metadata.name,
    });
    selectedPost.value = nextPost;
    return;
  }
  if (index === posts.value.length - 1 && hasNext) {
    page.value++;
    await refetch();
    selectedPost.value = posts.value[0].post;
  }
};

const checkSelection = (post: Post) => {
  return (
    post.metadata.name === selectedPost.value?.metadata.name ||
    selectedPostNames.value.includes(post.metadata.name)
  );
};

const handleCheckAllChange = (e: Event) => {
  const { checked } = e.target as HTMLInputElement;

  if (checked) {
    selectedPostNames.value =
      posts.value?.map((post) => {
        return post.post.metadata.name;
      }) || [];
  } else {
    selectedPostNames.value = [];
  }
};

const handleDeleteInBatch = async () => {
  Dialog.warning({
    title: t("core.post.operations.delete_in_batch.title"),
    description: t("core.post.operations.delete_in_batch.description"),
    confirmType: "danger",
    confirmText: t("core.common.buttons.confirm"),
    cancelText: t("core.common.buttons.cancel"),
    onConfirm: async () => {
      const chunks = chunk(selectedPostNames.value, 5);

      for (const chunk of chunks) {
        await Promise.all(
          chunk.map((name) => {
            return consoleApiClient.content.post.recyclePost({
              name,
            });
          })
        );
      }

      await refetch();
      selectedPostNames.value = [];

      Toast.success(t("core.common.toast.delete_success"));
    },
  });
};

const handlePublishInBatch = async () => {
  Dialog.info({
    title: t("core.post.operations.publish_in_batch.title"),
    description: t("core.post.operations.publish_in_batch.description"),
    confirmText: t("core.common.buttons.confirm"),
    cancelText: t("core.common.buttons.cancel"),
    onConfirm: async () => {
      const chunks = chunk(selectedPostNames.value, 5);

      for (const chunk of chunks) {
        await Promise.all(
          chunk.map((name) => {
            return consoleApiClient.content.post.publishPost({ name });
          })
        );
      }

      await refetch();
      selectedPostNames.value = [];

      Toast.success(t("core.common.toast.publish_success"));
    },
  });
};

const handleCancelPublishInBatch = async () => {
  Dialog.warning({
    title: t("core.post.operations.cancel_publish_in_batch.title"),
    description: t("core.post.operations.cancel_publish_in_batch.description"),
    confirmText: t("core.common.buttons.confirm"),
    cancelText: t("core.common.buttons.cancel"),
    onConfirm: async () => {
      const chunks = chunk(selectedPostNames.value, 5);

      for (const chunk of chunks) {
        await Promise.all(
          chunk.map((name) => {
            return consoleApiClient.content.post.unpublishPost({ name });
          })
        );
      }

      await refetch();
      selectedPostNames.value = [];

      Toast.success(t("core.common.toast.cancel_publish_success"));
    },
  });
};

// Batch settings
const batchSettingModalVisible = ref(false);
const batchSettingPosts = ref<ListedPost[]>([]);

function handleOpenBatchSettingModal() {
  batchSettingPosts.value = selectedPostNames.value.map((name) => {
    return posts.value?.find((post) => post.post.metadata.name === name);
  }) as ListedPost[];

  batchSettingModalVisible.value = true;
}

function onBatchSettingModalClose() {
  batchSettingModalVisible.value = false;
  batchSettingPosts.value = [];
}

watch(
  () => selectedPostNames.value,
  (newValue) => {
    checkedAll.value = newValue.length === posts.value?.length;
  }
);
</script>
<template>
  <PostSettingModal
    v-if="settingModal"
    :post="selectedPost"
    @close="onSettingModalClose"
  >
    <template #actions>
      <span @click="handleSelectPrevious">
        <IconArrowLeft v-tooltip="$t('core.common.buttons.previous')" />
      </span>
      <span @click="handleSelectNext">
        <IconArrowRight v-tooltip="$t('core.common.buttons.next')" />
      </span>
    </template>
  </PostSettingModal>
  <PostBatchSettingModal
    v-if="batchSettingModalVisible"
    :posts="batchSettingPosts"
    @close="onBatchSettingModalClose"
  />
  <VPageHeader :title="$t('core.post.title')">
    <template #icon>
      <IconBookRead />
    </template>
    <template #actions>
      <VButton :route="{ name: 'Categories' }" size="sm">
        {{ $t("core.post.actions.categories") }}
      </VButton>
      <VButton :route="{ name: 'Tags' }" size="sm">
        {{ $t("core.post.actions.tags") }}
      </VButton>
      <VButton :route="{ name: 'DeletedPosts' }" size="sm">
        {{ $t("core.post.actions.recycle_bin") }}
      </VButton>

      <VButton
        v-permission="['system:posts:manage']"
        :route="{ name: 'PostEditor' }"
        type="secondary"
      >
        <template #icon>
          <IconAddCircle />
        </template>
        {{ $t("core.common.buttons.new") }}
      </VButton>
    </template>
  </VPageHeader>

  <div class="m-0 md:m-4">
    <VCard :body-class="['!p-0']">
      <template #header>
        <div class="block w-full bg-gray-50 px-4 py-3">
          <div
            class="relative flex flex-col flex-wrap items-start gap-4 sm:flex-row sm:items-center"
          >
            <div
              v-permission="['system:posts:manage']"
              class="hidden items-center sm:flex"
            >
              <input
                v-model="checkedAll"
                type="checkbox"
                @change="handleCheckAllChange"
              />
            </div>
            <div class="flex w-full flex-1 items-center sm:w-auto">
              <SearchInput v-if="!selectedPostNames.length" v-model="keyword" />
              <VSpace v-else>
                <VButton @click="handlePublishInBatch">
                  {{ $t("core.common.buttons.publish") }}
                </VButton>
                <VButton @click="handleCancelPublishInBatch">
                  {{ $t("core.common.buttons.cancel_publish") }}
                </VButton>
                <VButton @click="handleOpenBatchSettingModal">
                  {{ $t("core.post.operations.batch_setting.button") }}
                </VButton>
                <VButton type="danger" @click="handleDeleteInBatch">
                  {{ $t("core.common.buttons.delete") }}
                </VButton>
              </VSpace>
            </div>
            <VSpace spacing="lg" class="flex-wrap">
              <FilterCleanButton
                v-if="hasFilters"
                @click="handleClearFilters"
              />
              <FilterDropdown
                v-model="selectedPublishStatus"
                :label="$t('core.common.filters.labels.status')"
                :items="[
                  {
                    label: t('core.common.filters.item_labels.all'),
                    value: undefined,
                  },
                  {
                    label: t('core.post.filters.status.items.published'),
                    value: `${postLabels.PUBLISHED}=true`,
                  },
                  {
                    label: t('core.post.filters.status.items.draft'),
                    value: `${postLabels.PUBLISHED}=false`,
                  },
                  {
                    label: t('core.post.filters.status.items.scheduling'),
                    value: `${postLabels.SCHEDULING_PUBLISH}=true`,
                  },
                ]"
              />
              <FilterDropdown
                v-model="selectedVisible"
                :label="$t('core.post.filters.visible.label')"
                :items="[
                  {
                    label: t('core.common.filters.item_labels.all'),
                    value: undefined,
                  },
                  {
                    label: t('core.post.filters.visible.items.public'),
                    value: 'PUBLIC',
                  },
                  {
                    label: t('core.post.filters.visible.items.private'),
                    value: 'PRIVATE',
                  },
                ]"
              />
              <CategoryFilterDropdown
                v-model="selectedCategory"
                :label="$t('core.post.filters.category.label')"
              />
              <TagFilterDropdown
                v-model="selectedTag"
                :label="$t('core.post.filters.tag.label')"
              />
              <HasPermission :permissions="['system:users:view']">
                <UserFilterDropdown
                  v-model="selectedContributor"
                  :label="$t('core.post.filters.author.label')"
                />
              </HasPermission>
              <FilterDropdown
                v-model="selectedSort"
                :label="$t('core.common.filters.labels.sort')"
                :items="[
                  {
                    label: t('core.common.filters.item_labels.default'),
                  },
                  {
                    label: t('core.post.filters.sort.items.publish_time_desc'),
                    value: 'spec.publishTime,desc',
                  },
                  {
                    label: t('core.post.filters.sort.items.publish_time_asc'),
                    value: 'spec.publishTime,asc',
                  },
                  {
                    label: t('core.post.filters.sort.items.create_time_desc'),
                    value: 'metadata.creationTimestamp,desc',
                  },
                  {
                    label: t('core.post.filters.sort.items.create_time_asc'),
                    value: 'metadata.creationTimestamp,asc',
                  },
                  {
                    label: t(
                      'core.post.filters.sort.items.last_modify_time_desc'
                    ),
                    value: 'status.lastModifyTime,desc',
                  },
                  {
                    label: t(
                      'core.post.filters.sort.items.last_modify_time_asc'
                    ),
                    value: 'status.lastModifyTime,asc',
                  },
                  {
                    label: t('core.post.filters.sort.items.visit_desc'),
                    value: 'stats.visit,desc',
                  },
                  {
                    label: t('core.post.filters.sort.items.comment_desc'),
                    value: 'stats.totalComment,desc',
                  },
                ]"
              />
              <div class="flex flex-row gap-2">
                <div
                  class="group cursor-pointer rounded p-1 hover:bg-gray-200"
                  @click="refetch()"
                >
                  <IconRefreshLine
                    v-tooltip="$t('core.common.buttons.refresh')"
                    :class="{ 'animate-spin text-gray-900': isFetching }"
                    class="h-4 w-4 text-gray-600 group-hover:text-gray-900"
                  />
                </div>
              </div>
            </VSpace>
          </div>
        </div>
      </template>
      <VLoading v-if="isLoading" />
      <Transition v-else-if="!posts?.length" appear name="fade">
        <VEmpty
          :message="$t('core.post.empty.message')"
          :title="$t('core.post.empty.title')"
        >
          <template #actions>
            <VSpace>
              <VButton @click="refetch">
                {{ $t("core.common.buttons.refresh") }}
              </VButton>
              <VButton
                v-permission="['system:posts:manage']"
                :route="{ name: 'PostEditor' }"
                type="secondary"
              >
                <template #icon>
                  <IconAddCircle />
                </template>
                {{ $t("core.common.buttons.new") }}
              </VButton>
            </VSpace>
          </template>
        </VEmpty>
      </Transition>
      <Transition v-else appear name="fade">
        <VEntityContainer>
          <PostListItem
            v-for="post in posts"
            :key="post.post.metadata.name"
            :post="post"
            :is-selected="checkSelection(post.post)"
            @open-setting-modal="handleOpenSettingModal"
          />
        </VEntityContainer>
      </Transition>

      <template #footer>
        <VPagination
          v-model:page="page"
          v-model:size="size"
          :page-label="$t('core.components.pagination.page_label')"
          :size-label="$t('core.components.pagination.size_label')"
          :total-label="
            $t('core.components.pagination.total_label', { total: total })
          "
          :total="total"
          :size-options="[20, 30, 50, 100]"
        />
      </template>
    </VCard>
  </div>
</template>
